package org.NooLab.itexx.tcp;

import java.io.*;
import java.net.*;
  

import org.NooLab.utilities.logging.LogControl;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.objects.StringedObjects;


/**
 * 
 * acts as a sender in the context of the MessageBoard, thus no "readobject" necessary,
 * as it would be possible in TCP connections, we just drop it to the Server
 * 
 * maybe, we just take a very short connection confirmation, which would be
 * an advantage over UDP  
 * 
 */
public class TcpClient implements DataSenderIntf{
	
	ClientMsgHandlerIntf clientMsg ;
	  
	Socket requestSocket;
	int remotePort = 7111;
	String remoteAddress = "localhost";
	
	ObjectOutputStream objoutStream;
	ObjectInputStream objinStream;
	
	String message;
	String name, description="";
	
	boolean closed = true, closing=false;
	ClientCommunicator communicator; 
	
	int lastSenderState=0;
	
	PrintLog out = new PrintLog(2,true);
	StringedObjects sob = new StringedObjects();

	private int silentShutDown;
	
	public TcpClient( int remoteport, int ctrlport, ClientMsgHandlerIntf clientmsg, String description ){
		
		remotePort = remoteport;
		 
		clientMsg = clientmsg;
		
		this.description = description;
		
		out.print(5, "TcpClient created(A).");
	}
	
	public TcpClient( String name, ClientMsgHandlerIntf clientmsg ) {
		
		clientMsg = clientmsg;
		this.name = name;
		
		out.print(5, "TcpClient created(B)."); 
	}

	public int activate( int activationCode ) throws Exception {
		
		int connectionState = -1;
		String connectExceptionStr="";
		int z;
		
		try {
			// 1. creating a socket to connect to the server  
												out.printErr(5,"\nTcpClient ("+description+") attempting to connect to "+ remoteAddress+":"+remotePort+"! "); 
			
			requestSocket = new Socket( remoteAddress, remotePort);
			// connecting to port 7112 closes it... ??
			 
			requestSocket.setReuseAddress(true);
			requestSocket.setTcpNoDelay(true);
												out.print(5,"C: Connected (requestSocket created) to remote address for sending : "+remoteAddress+" in port "+remotePort);
if (remotePort==7200){
	z=0;
}
			// 2. get Input and Output streams
			objoutStream = new ObjectOutputStream(requestSocket.getOutputStream());
			objoutStream.flush();
			// why does this fail...
			// objinStream = new ObjectInputStream( requestSocket.getInputStream() );

			closed = false;
			
			// 3: Communicating with the server, which is done by an interfaced object
			//    == start the communication thread, which takes any prepared message and sends it to the server
												out.print(5,"starting communicator thread...");
			startCommunicator();
			z=0;
			while ((z<300) && ( communicator.isrunning==false)){
				// waiting until it has been started
				delay(3);
				z++;
			}
			out.print(5,"communicator is now running : "+communicator.isrunning);
			if (communicator.isrunning==false){
				throw(new Exception("Communicator-thread did not start!!!"));
			}
			try {
												out.print(5,"starting communicator successful ? "+ communicator.isrunning);
				// message = (String) objinStream.readObject();
												// out.print(4,"c <- s (1): " + message);
				// we should parse the return for action codes
		        // "Connection to client 1 successfully established."
			    // ... and better messages by the server (incl action codes)
								
                connectionState = 0;
                
			} catch (Exception classNot) { // ClassNotFoundException 
				System.err.println("data received in unknown format");
			}
			
		} catch( ConnectException cex){
			connectExceptionStr = cex.getMessage() ;
			if (LogControl.Level >=3){
				System.err.println("ConnectException (remoteAddress: "+remoteAddress+", remotePort: "+remotePort+"): "+connectExceptionStr);
			}
			connectionState = -2;
			
		} catch (UnknownHostException unknownHost) {
			connectionState = -7;
			System.err.println("You are trying to connect to an unknown host!");
		} catch (IOException ioException) {
			connectionState = -3;
			String estr = "(no info about requestSocket(null))";
			if (requestSocket!=null){
				estr = 	"requestSocket: "+requestSocket.toString()+
						"    connected: "+requestSocket.isConnected()+
						"    connected: "+requestSocket.getPort() ;
			}
			System.err.println( "Critical IOException ("+
								ioException.getMessage()+") while activating a TcpClient for "+ 
								remoteAddress+":"+remotePort+"! \n"+ estr);
			
			if (out.getPrintlevel()>=4){
				ioException.printStackTrace();
			}
		}  
		
		return connectionState;
	}

	@Override
	public int getLastState(String suid) {
		 
		return lastSenderState;
	}

	@Override
	public int getLastSenderState() {
		return lastSenderState;
	}

	@Override
	public void send(Object obj) {

		send(obj, "");
		
	}

	public void send(Object obj, String suid) {
		
		String stringedObject="", actualMessage ; 
		int connstate;
			
		try{
												out.print(5,"TcpClient going to send (context: "+description+") ...");
												int sp=-1;
												if (requestSocket!=null){ sp=requestSocket.getLocalPort();}
												
												out.print(5,"          activate slot (local port:"+sp+"), heading for remote port "+remotePort+" ...");
		    connstate = activate( 2 ); delay(2);
		    // calling this closes 7112 ??? 
			
		 
		    stringedObject = (String)obj;
												out.print(5,"          message set to tcp engine...");
		    if (communicator==null){
		    	out.printErr(4,"No send(), since TCP - communicator was unexpectedly = null.");
		    	
		    	// TODO: should be linked to suid !!
		    	lastSenderState = __ERR_BASENUM_TCP + connstate ; // __SEND_ERR_ERROR ;
		    	return;
		    }
		    lastSenderState = 0 ;
		    
		    if (suid.length()==0){
		    	suid="0" ;
		    } 

		    actualMessage = __TemplateMsg.replace("#SUID#",suid);
		    actualMessage = actualMessage.replace("#DATA#",stringedObject);
		    
		    
		    String encodedMsg = sob.encode(actualMessage);  
		    communicator.setMessage( encodedMsg ) ;
		
		    int z=0;
		    while ((communicator.responseOk==false) && (z<1)){ // ?? why waiting anyway ?
		    	delay(1); z++;
		    }
		 
												out.print(5,"          closing connection to "+remotePort+"...");
		close(0);
												out.print(5,"TcpClient ("+description+"): sent, done & leaving.");
		}catch(Exception e){
			e.printStackTrace();
		}
		return ;
	}

	public void close(int closeServer){
		closing = true;
		
		if (closeServer>0){
			if (communicator!=null){
				communicator.setMessage("bye"); // get it from the ClientMsgHandler !
				
				int z = 0;
				while ((z < 50) && (communicator.responseOk == false)) {
					delay(3);z++;
				}
				communicator.isrunning=false;
			}
		} else{
			// signaling a "closed" to the server, which causes the server to close the 
			// ClientHandlerThread, but not to close itself !
			if (communicator!=null){
				communicator.setMessage("closed");
				int z=0;
				while ((communicator.responseOk==false)&& (z<=2)){
					delay(2);
					z++;
				}
			}
		}
		
		if (communicator!=null){
			                            out.print(5, "closing and stopping communicator...");
			communicator.isactivated=false ;
			communicator.isrunning=false;
			
			communicator.stop();
		}
				
		// 4: Closing connection
		try {
			if (objinStream!=null){
				objinStream.close();
			}
			
			if (objoutStream!=null){
				objoutStream.close();
			}
			if (requestSocket!=null){
				requestSocket.close();
			}
		} catch (SocketException sex) {
			// sex.printStackTrace();
			// we are closing anyway...
		} catch (IOException ioException) {
			// ioException.printStackTrace();
			// we are closing anyway...
		}finally{ 
			closed = true;
		}
		
	}

 
	public void release() {
		 
		close(1);
		out.print(5, "TcpClient released.");
	}

	@Override
	public void setTimeOut(int value) {
	
		
	}

	@SuppressWarnings("static-access")
	private void delay(int millis){
	
		try{
			Thread.currentThread().sleep(millis) ;
		}catch(Exception e){}
	}
	
	

 
	private void startCommunicator(){
												out.print(5,"starting ClientCommunicator.");
		communicator = new ClientCommunicator();
	}
	

	public void performTest(){
		for (int i=0;i<20;i++){
		
			communicator.setMessage("Hi my server ("+i+")...");
	
			delay(205);
			// message = "bye";
			// sendMessage(message);
			communicator.setMessage("...what are you doing?");
			delay(794);
		}
	}



	class ClientCommunicator implements Runnable{
		
		String currentMessage = "";
		boolean isrunning=false, isactivated=false;
		Thread cthrd;
		boolean responseOk;
		
		public ClientCommunicator(){
			
			cthrd = new Thread(this,"ClientCommunicator - "+description);
			cthrd.start() ;
		}

		public void setMessage(String msg){
			responseOk = false;
			
			currentMessage = msg;
			
			int z=0;
			while ((responseOk==false) & (z<500)){
				delay(2); 
				z++;
			}
		}
		
		public void stop(){
			try {
				
				// cthrd.join();
				
			} catch (Exception e) {}
			
		}
		@SuppressWarnings("static-access")
		public void run() {
			isrunning=true;
			try{
				
				while (isrunning){

					if (isactivated==false){
						
						if (currentMessage.length()>0){
							isactivated = true;
							// do it
												out.print(5,"     c -> s (len="+currentMessage.length()+")...");
												// currentMessage ="brutally short string for test...";
							sendMessage(currentMessage);
							
												out.print(5,"     c -> s, waiting for response... ");
												out.print(5,"        (communicator is running:"+communicator.isrunning+")\n" +
														    "        socket (local:"+requestSocket.getLocalPort()+") is connected to remote port "+remotePort+") : "+ requestSocket.isConnected()+"\n" );
							//message = (String) objinStream.readObject();
												//out.printErr(4,"after sending ::  c <- s (2):  " + message+"\n");
							
							responseOk = true;
							currentMessage = "" ;
						}
						isactivated = false;
					}else{
						Thread.currentThread().sleep(0,200) ;
					}
					
				}// while isrunning
				
				 
			// }catch(Exception eofx){ // EOFException 
				
			// }catch(java.net.SocketException sex){
				// out.printErr(2, "SocketException: "+sex.getMessage()) ;
			}catch(Exception e){
				e.printStackTrace();
			}
			out.print(5,"C (sender): leaving communication loop...\n");
		}
		
		protected void sendMessage(String msg) {
			try {
				
												out.print(5,"TcpClient: send msg c -> board");
												out.print(6,"           " + msg);
												
				objoutStream.writeObject(msg);
				objoutStream.flush();
												out.print(5,"TcpClient: msg sent." );
				isrunning=false;
				
			} catch(SocketException sex){
				System.err.println("Critical SocketException in sendMessage(): "+sex.getMessage() );
				
			} catch (IOException ioException) {
				ioException.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	} // end of inner class



	@Override
	public void setSilentShutDown(int silent) {
		
		silentShutDown = silent;
		if (silent>=1){
			out = new PrintLog(6,true);
		}

	}
}





